<?php

namespace app\common\model\banxi\word;

use app\common\enums\ResourcesTypeEnum;
use app\common\model\common\BaseModel;
use app\common\service\TTLService;
use app\common\service\UploadService;
use think\db\exception\PDOException;
use think\exception\ValidateException;
use think\facade\Cache;
use think\model\concern\SoftDelete;

/**
 * 单词资源模型
 */
class WordResources extends BaseModel
{
    use SoftDelete;

    protected $name = 'banxi_word_resources';

    protected $autoWriteTimestamp = true;

    protected $deleteTime = 'del';

    protected $defaultSoftDelete = 0;

    protected $append = [
        'resourcesTypeCn'
    ];

    protected $type = [
        'id' => 'string',
        'del' => 'string',
        'isVocal' => 'boolean',
        'createTime' => 'timestamp:Y-m-d H:i:s',
        'updateTime' => 'timestamp:Y-m-d H:i:s',
    ];

    //-----------------------------------------------------------------------------------
    //｜                                   获取器                                       ｜
    //-----------------------------------------------------------------------------------

    public function getResourcesTypeCnAttr($value, $data)
    {
        $value = $value ?: ($data['resourcesType'] ?? '');
        return (new ResourcesTypeEnum())->getDescription($value) ?? '';
    }

    //-----------------------------------------------------------------------------------
    //｜                                   处理方法                                       ｜
    //-----------------------------------------------------------------------------------

    /**
     * 批量更新资源数据
     *
     * @param array $texts 资源文本数组
     * @param string|int $resourcesType 资源类型
     * @return bool
     * @throws ValidateException
     */
    public function updateFill(array $texts, string|int $resourcesType): bool
    {
        $key = "WordResources:updateFill:" . $resourcesType;

        // 检查是否正在处理相同类型的资源更新
        if (Cache::has($key)) {
            throw new ValidateException('正在更新，请稍后重试');
        }

        // 设置5秒缓存锁
        Cache::set($key, 1, 5);

        try {
            // 过滤并去重文本数据
            $texts = array_filter($texts, function ($item) {
                return !empty(trim($item));
            });

            $textSet = array_unique($texts);

            if (empty($textSet)) {
                return true;
            }

            // 查找已存在的资源
            $existList = $this->where([
                ['text', 'in', $textSet],
                ['resourcesType', '=', $resourcesType],
            ])->column('text', 'id');

            // 提取已存在的文本
            $existTexts = array_values($existList);

            // 找出需要新增的文本
            $newTexts = array_diff($textSet, $existTexts);

            if (empty($newTexts)) {
                return true;
            }

            // 构建新增数据
            $newList = [];
            foreach ($newTexts as $text) {
                $newList[] = [
                    'text' => $text,
                    'resourcesType' => $resourcesType,
                    'isVocal' => 0
                ];
            }

            // 批量保存新资源
            $this->saveAll($newList);

            return true;
        } catch (PDOException $e) {
            throw new ValidateException('数据库操作失败: ' . $e->getMessage());
        } catch (\Exception $e) {
            throw new ValidateException('操作失败: ' . $e->getMessage());
        } finally {
            // 清除缓存锁
            Cache::delete($key);
        }
    }

    public function audioCompositing()
    {
        $count = 0;
        try {
            $totalCount = $this->where(['isVocal' => 0])->count();
            $chunkSize = 50;
            $totalPages = ceil($totalCount / $chunkSize);

            // 复用 TTLService 实例
            $TTLService = new TTLService();
            for ($page = 1; $page <= $totalPages; $page++) {
                $words = $this->where(['isVocal' => 0])
                    ->field('id,text,resourcesType')
                    ->page($page, $chunkSize)
                    ->select()->toArray();

                if (empty($words)) {
                    continue;
                }

                foreach ($words as $item) {
                    try {
                        $text = $item['text'];
                        $resourcesType = $item['resourcesType'];

                        // 验证文本是否为空
                        if (empty($text)) {
                            logSave([
                                'file' => __FILE__,
                                'message' => 'Skipping empty text resource',
                                'item_id' => $item['id'],
                                'resources_type' => $resourcesType
                            ], 'audioCompositing');
                            continue;
                        }

                        // 根据资源类型处理音频合成
                        switch ($resourcesType) {
                            case ResourcesTypeEnum::E1:
                            case ResourcesTypeEnum::E3:
                                $res = $TTLService->getSyllableVocalToUrl($text);
                                break;
                            case ResourcesTypeEnum::E4:
                                $res = $TTLService->getEnVocalToUrl($text);
                                break;
                            default:
                                logSave([
                                    'file' => __FILE__,
                                    'message' => 'Unsupported resource type',
                                    'item_id' => $item['id'],
                                    'text' => $text,
                                    'resources_type' => $resourcesType
                                ], 'audioCompositing');
                                continue 2;
                        }

                        if (!$res) {
                            logSave([
                                'file' => __FILE__,
                                'message' => 'Word vocal synthesis failed',
                                'item_id' => $item['id'],
                                'text' => $text,
                                'resources_type' => $resourcesType,
                                'error' => $TTLService->getError()
                            ], 'audioCompositing');
                            continue;
                        }

                        $count++;
                        // 更新资源记录
                        $this->update([
                            'vocalUrl' => $res,
                            'isVocal' => 1,
                        ], ['id' => $item['id']]);

                        logSave([
                            'file' => __FILE__,
                            'message' => 'Word vocal synthesis Finished',
                            'item_id' => $item['id'],
                            'text' => $text,
                            'resources_type' => $resourcesType,
                        ], 'audioCompositing');
                    } catch (\Exception|PDOException|ValidateException $e) {
                        logSave([
                            'file' => __FILE__,
                            'message' => 'Single word vocal synthesis failed',
                            'item_id' => $item['id'] ?? 'unknown',
                            'text' => $item['text'] ?? 'unknown',
                            'resources_type' => $item['resourcesType'] ?? 'unknown',
                            'error' => $e->getMessage(),
                            'trace' => $e->getTraceAsString()
                        ], 'audioCompositing');
                        continue;
                    }
                }
            }
        } catch (\Exception $e) {
            logSave([
                'file' => __FILE__,
                'message' => 'audioCompositing error',
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'audioCompositing');
        }

        logSave([
            'file' => __FILE__,
            'message' => 'Audio compositing completed',
            'processed_count' => $count
        ], 'audioCompositing');

        return $count;
    }


    public function getVocal(string $text, string $resourcesType)
    {
        $resourcesTypeEnum = new ResourcesTypeEnum();
        if (!$resourcesTypeEnum->isRealtimeGet($resourcesType)) {
            throw new ValidateException('该资源类型不支持实时获取');
        }
        $resources = $this->where([
            'text' => $text,
            'resourcesType' => $resourcesType
        ])->order(['id' => 'desc'])->find();
        if ($resources && $resources['vocalUrl']) {
            $uploadService = new UploadService();
            return $uploadService->downloadBody($resources['vocalUrl']);
        }

        $bytes = null;
        $TTLService = new TTLService();
        // 根据资源类型处理音频合成
        switch ($resourcesType) {
            case ResourcesTypeEnum::E1:
            case ResourcesTypeEnum::E3:
                $bytes = $TTLService->getSyllableVocalToByte($text);
                break;
            case ResourcesTypeEnum::E4:
                $bytes = $TTLService->getEnVocalToByte($text);
                break;
        }
        if ($bytes) {
            $url = $TTLService->upload($text, $bytes);
            if ($resources) {
                $resources->text = $text;
                $resources->vocalUrl = $url;
                $resources->isVocal = 1;
                $resources->resourcesType = $resourcesType;
                $resources->save();
            } else {
                $this->save([
                    'text' => $text,
                    'vocalUrl' => $url,
                    'isVocal' => 1,
                    'resourcesType' => $resourcesType
                ]);
            }


            return $bytes;
        }
        throw new ValidateException('合成失败');


    }


    //-----------------------------------------------------------------------------------
    //｜                                   模型关联                                       ｜
    //-----------------------------------------------------------------------------------
}
